﻿using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace KaroGame.FrontEnd
{
    class KaroBoundingBox
    {
        #region Properties
        private BasicEffect effect;
        private GraphicsDevice GraphicsDevice;
        private BoundingBox boundingBox;
        private IndexBuffer indexBuffer;
        private short[] index;
        private VertexBuffer vertexBuffer;
        private VertexPositionColor[] vertex;
        private Vector3[] vectors;
        private Color currentColor;

        private static Color activeColor = Color.Red;
        private static Color nActiveColor = Color.Yellow;
        #endregion

        #region Constructors
        public KaroBoundingBox(BoundingBox baseBoundingBox, BasicEffect Effect, GraphicsDevice Device)
        {
            effect = Effect;
            GraphicsDevice = Device;
            boundingBox = baseBoundingBox;
            currentColor = nActiveColor;

            // creating the bounding box index buffer
            index = new short[] { 3, 2, 0, 1, 4, 5, 6, 2, 7, 3, 4 };
            indexBuffer = new IndexBuffer(GraphicsDevice, sizeof(short) * index.Length, BufferUsage.None, IndexElementSize.SixteenBits);
            indexBuffer.SetData<short>(index);
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Calculates vertices of the bounding box after given transformation
        /// </summary>
        /// <returns></returns>
        public BoundingBox GetRealBoundingBox(Matrix transformation)
        {
            List<Vector3> corners = new List<Vector3>();
            foreach (Vector3 corner in boundingBox.GetCorners())
            {
                corners.Add( Vector3.Transform( corner, transformation ) );
            }

            return BoundingBox.CreateFromPoints( corners );
        }

        /// <summary>
        /// Draws the bounding box after the given transformation
        /// </summary>
        /// <param name="transformation"></param>
        public void Draw(Matrix transformation)
        {
            vectors = new Vector3[8];
            vertex = new VertexPositionColor[8];

            Matrix savedGameWorld;

            vectors = boundingBox.GetCorners();

            for (int i = 0; i < vectors.Length; i++)
                vertex[i] = new VertexPositionColor(Vector3.Transform(vectors[i], transformation), currentColor);

            vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionColor.SizeInBytes * 8, BufferUsage.None);
            vertexBuffer.SetData<VertexPositionColor>(vertex);

            GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);
            GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionColor.SizeInBytes);
            GraphicsDevice.Indices = indexBuffer;
            GraphicsDevice.RenderState.CullMode = CullMode.None;
            GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;

            savedGameWorld = effect.World;
            effect.World = Matrix.Identity;
            effect.VertexColorEnabled = true;

            effect.Begin();

            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, vertex.Length, 0, index.Length - 2);
                pass.End();
            }

            effect.End();
            effect.World = savedGameWorld;

            GraphicsDevice.RenderState.FillMode = FillMode.Solid;
        }

        /// <summary>
        /// Checks whether the given ray intersects with the bounding box
        /// </summary>
        public float? Intersects(Ray mouseRay, Matrix tranformation)
        {
            return GetRealBoundingBox(tranformation).Intersects(mouseRay);
        }

        /// <summary>
        /// Selects the bounding box
        /// </summary>
        public void Activate()
        {
            currentColor = activeColor;
        }

        /// <summary>
        /// Deselects the bounding box
        /// </summary>
        public void Deactivate()
        {
            currentColor = nActiveColor;
        }
        #endregion
    }
}
